package com.lhh.utils;

import cn.hutool.core.codec.Base64;
import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author czx
 * @date 2021/05/10
 */
public class JwtUtil {
    /**
     * 请求头中的令牌标识
     */
    public static final String TOKEN_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Bearer";
    /**
     * access_token 默认过期时间 单位秒.
     */
    public static final long ACCESS_TOKEN_EXPIRED_TIME = 60 * 60L;
    /**
     * refresh_token的过期时间
     */
    public static final long REFRESH_TOKEN_EXPIRED_TIME = 7 * 24 *60 * 60L;
    public static final String jwtId = "test";
    /**
     * jwt 加密解密密钥
     */
    private static final String JWT_SECRET = "test1234";

    public static String createJwt(Map<String, Object> claims) {
        return createJwt(claims, ACCESS_TOKEN_EXPIRED_TIME);
    }

    public static String createJwt(Map<String, Object> claims, long expire) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; //指定签名的时候使用的签名算法，也就是header那部分

        SecretKey secretKey = createKey();
        long nowMillis = System.currentTimeMillis();  //生成JWT的时间
        //为payload添加各种标准声明和私有声明
        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)          //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setId(jwtId)               //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(new Date())           //jwt的签发时间
                .signWith(signatureAlgorithm, secretKey);//设置签名使用的签名算法和签名使用的秘钥
        long expMillis = nowMillis + expire * 1000;
        Date exp = new Date(expMillis);
        builder.setExpiration(exp);     //设置过期时间
        return builder.compact();
    }


    /**
     * 验证jwt
     * 解析jwt的时候会抛出一些异常, 由外部调用方法捕获
     * @param token
     * @return
     */
    public static Claims verifyJwt(String token) {
        //签名秘钥，和生成的签名的秘钥一样
        SecretKey key = createKey();
        Claims claims = Jwts.parser()
                    .setSigningKey(key)         //设置签名的秘钥
                    .parseClaimsJws(token).getBody();
        return claims;
    }

    /**
     * 检查jwt是否过期
     * @return
     */
    public static boolean isExpire(String token) {
        SecretKey key = createKey();
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(key)         //设置签名的秘钥
                    .parseClaimsJws(token).getBody();
            return false;
        } catch (ExpiredJwtException e) {
            return true;
        }
    }

    /**
     * 由字符串生成加密key
     *
     * @return
     */
    public static SecretKey createKey() {
        byte[] encodedKey = Base64.decode(JWT_SECRET);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 根据userId和openid生成token
     */
    public static String createToken(String openId, Integer userId) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("openId", openId);
        claims.put("sub", openId);
        return createJwt(claims, ACCESS_TOKEN_EXPIRED_TIME);
    }

    public static void main(String[] args) {
        String s = createToken("111", 20);
        System.out.println(s);

        String token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMTEiLCJvcGVuSWQiOiIxMTEiLCJleHAiOjE2MjA2MDk2NzMsInVzZXJJZCI6MjAsImlhdCI6MTYyMDYwOTA2OCwianRpIjoiZHlAMTIzNDU2In0.0ypSu5fEY8o-SmNO--0qXMV-nLINVQublqw-Cpky5nM";
        Claims claims = verifyJwt(token);
        String subject = claims.getSubject();
        Integer userId = (Integer) claims.get("userId");
        String openId = (String) claims.get("openId");
        String sub = (String) claims.get("sub");
        System.out.println("subject:" + subject);
        System.out.println("userId:" + userId);
        System.out.println("openId:" + openId);
        System.out.println("sub:" + sub);
    }
}
